#include <stdlib.h>
#include <string.h>

#include "list.h"

List *list_init(listdestroy dispose, elmtmatch compare)
{
    List *list = (List *)malloc(sizeof(struct List_));
    if(list == NULL) return NULL;

    list->size = 0;
    list->destroy = dispose;
    list->match = compare;
    list->head = NULL;
    list->tail = NULL;
    return list;
}

void list_destroy(List *list)
{
    void *data;
    while(list_size(list) > 0)
    {
        if(list_rem_next(list, NULL, (void **)&data) == 0 && list->destroy != NULL)
        {
            list->destroy(data);
        }
    }

    memset(list, 0, sizeof(List));
    free(list);
}

int list_ins_next(List *list, ListElmt *element, const void *data)
{
    ListElmt *new_element = (ListElmt *)malloc(sizeof(ListElmt));
    if(new_element == NULL)
        return -1;

    new_element->data = (void *)data;
    if(element == NULL)
    {
        if(list_size(list) == 0)
            list->tail = new_element;

        new_element->next = list->head;
        list->head = new_element;
    }
    else
    {
        if(element->next == NULL)
            list->tail = new_element;

        new_element->next = element->next;
        element->next = new_element;
    }
    list->size++;

    return 0;
}

int list_rem_next(List *list, ListElmt *element, void **data)
{
    ListElmt *old_element;

    // Do not allow removal from an empty list.
    if(list_size(list) == 0)
        return -1;

    // Remove the element from the list.
    if(element == NULL)
    {
        *data = list->head->data;
        old_element = list->head;
        list->head = list->head->next;

        if(list_size(list) == 1)
            list->tail = NULL;
    }
    else
    {
        if(element->next == NULL)
            return -1;

        *data = element->next->data;
        old_element = element->next;
        element->next = element->next->next;

        if(element->next == NULL)
            list->tail = element;
    }

    free(old_element);
    list->size--;

    return 0;
}
